import ctypes
from common.constants import Constants

class VMAPI(object):
    """
    initiates vix api
    """
    def __init__(self,context):
        self.context = context
        vix = ctypes.CDLL(self.context.vix_path)
        self.host = Host(vix,context)
        self.host.connect_host()

class Host(object):
    """
    connects to the host machine
    """
    def __init__(self,vix,context):
        self.options = 0
        self.vix = vix
        self.host_handle = ctypes.c_int()
        self.context = context
        self.__vm_list = []

        self.VIX_OK = 0

    def connect_host(self):
        job_handle = self.vix.VixHost_Connect(-1,
                                              10,
                                              self.context.vix.server.encode(),
                                              0,
                                              self.context.vix.user.encode(),
                                              self.context.vix.password.encode(),
                                              0,
                                              0,
                                              None,
                                              None)
        err = self.vix.VixJob_Wait(job_handle,
                                   3010,
                                   ctypes.byref(self.host_handle),
                                   0)
        self.vix.Vix_ReleaseHandle(job_handle)        
        if err != self.VIX_OK:
            if err == 16:
                message = "connectHost: server host name is incorrect."
            elif err == 3014:
                message = "connectHost: incorrect credentials to the host."
            else:
                message = "connectHost: connection to host failed. Error code: "+str(err)            
            self.vix.Vix_ReleaseHandle(self.host_handle)
            raise VMError(message)

    def discovery_proc(self,job,event_type,event_info,client_data):
        url = ctypes.c_char_p()
        if event_type != 8:
            return
        err = self.vix.Vix_GetProperties(event_info,
                                         4010,
                                         ctypes.byref(url),
                                         0)
        if err != 0:
            raise VMError('error code" {}'.format(err))
        else:
            self.__vm_list.append(url.value)

        self.vix.Vix_FreeBuffer(url)

    def _get_vmx_running_list(self):
        CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p,
                                   ctypes.c_int,
                                   ctypes.c_int,
                                   ctypes.c_int,
                                   ctypes.c_void_p)
        cmp_func = CMPFUNC(self.discovery_proc)
        job_handle = self.vix.VixHost_FindItems(self.host_handle,
                                                1,
                                                0, 
                                                -1, 
                                                cmp_func,
                                                None)
        err = self.vix.VixJob_Wait(job_handle, 0)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != 0:
            raise VMError('error code {0}'.format(err))
        return self.__vm_list

    def open_vm(self,vm_alias):
        """
        opens vm machine by the alias found in the ini file
        """
        return VM(self.vix,self.context,self.host_handle,vm_alias)

    def close(self):
        """
        closes opened vm hangles
        """
        self.vix.Vix_ReleaseHandle(self.host_handle)

    def _get_vm_alias_by_vmx_path(self,vmx_path):
        """
        trying to find if gived vmx_path has vm alias
        """
        vms = self.context.vm.__dict__
        for vm in vms:
            if vmx_path == vms[vm]:
                return vm

    def get_running_vms(self):
        """
        detects vm alias by vmx path
        """
        vm_list = []
        for item in self._get_vmx_running_list():
            vm_alias = self._get_vm_alias_by_vmx_path(item.decode())
            if vm_alias is not None:
                vm_list.append(vm_alias)
        return vm_list
    
    def __del__(self):
        self.vix.VixHost_Disconnect(self.host_handle)

class VM(object):

    def __init__(self,vix,context,host_handle,vm_alias):
        self.options = 0
        self.vix = vix
        self.host_handle = host_handle
        self.vm_handle = ctypes.c_int()
        self.context = context
        self.snapshot_handle = ctypes.c_int()

        self.VIX_OK = 0
        if vm_alias in self.context.vm.__dict__:
            vmx_path = self.context.vm.__dict__[vm_alias]
        else:
            raise VMError('there is no vm image with alias: {0}'.format(vm_alias))
        job_handle = self.vix.VixVM_Open(self.host_handle,
                                         vmx_path.encode(),
                                         None,
                                         None)

        err = self.vix.VixJob_Wait(job_handle,
                                   3010,
                                   ctypes.byref(self.vm_handle),
                                   None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            if err == 4:                
                message = "openGuest: path to the vmx file is incorrect."
            else:
                message = "openGuest: openning of the VM failed. Error code: "+str(err)
            self.vix.Vix_ReleaseHandle(self.vm_handle)                
            raise VMError(message)

    def power_off(self):
        """
        turn vitrual machine power off
        """
        job_handle = self.vix.VixVM_PowerOff(self.vm_handle,
                                             0,
                                             None,
                                             None)

        err = self.vix.VixJob_Wait(job_handle,
                                   None);
        self.vix.Vix_ReleaseHandle(job_handle)        
        if err != self.VIX_OK:                         
            raise VMError('something wrong, error: {0}'.format(err))

    def power_on(self):
        """
        turn vitrual machine power on
        """
        job_handle = self.vix.VixVM_PowerOn(self.vm_handle,
                                             0,
                                             None,
                                             None)

        err = self.vix.VixJob_Wait(job_handle,
                                   None);
        self.vix.Vix_ReleaseHandle(job_handle)        
        if err != self.VIX_OK:                         
            raise VMError('something wrong, error: {0}'.format(err))

    def revert_snapshot(self, name = None):
        """
        reverts snapshot
        """
        snapshot_index = 0;
        if name is None:
            err = self.vix.VixVM_GetRootSnapshot(self.vm_handle,
                                                 snapshot_index,
                                                 ctypes.byref(self.snapshot_handle))
        else:
            err = self.vix.VixVM_GetNamedSnapshot(self.vm_handle,
                                                  name.encode(),
                                                  ctypes.byref(self.snapshot_handle))            
        if err != self.VIX_OK:
            message = 'snapshot error {0}'.format(err)
            raise VMError(message)

        job_handle = self.vix.VixVM_RevertToSnapshot(self.vm_handle,
                                                     self.snapshot_handle,
                                                     0,
                                                     0,
                                                     None,
                                                     None)

        err = self.vix.VixJob_Wait(job_handle, None)
        self.vix.Vix_ReleaseHandle(job_handle)
        self.vix.Vix_ReleaseHandle(self.snapshot_handle)
        if err != self.VIX_OK:
            message = 'snapshot error {0}'.format(err)
            raise VMError(message)
        
    def close(self):
        """
        closes opened vm hangles
        """
        self.vix.Vix_ReleaseHandle(self.vm_handle)
        
    def open_guest(self, user, password):
        """
        returns guest object to make some actions with guest machine
        """
        self._login_in_guest(user, password)
        return Guest(self.vix, self.vm_handle)
    
    def _login_in_guest(self, user, password):
        """
        logs to guest
        """
        job_handle = self.vix.VixVM_LoginInGuest(self.vm_handle,
                                                 user.encode(),
                                                 password.encode(),
                                                 0,
                                                 None,
                                                 None)
        err = self.vix.VixJob_Wait(job_handle, None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'log in to guest error {0}'.format(err)
            raise VMError(message) 	 
                
    def wait_for_tools_in_guest(self, timeout):
        """
        waits for tools to be responded from guest
        """
        job_handle = self.vix.VixVM_WaitForToolsInGuest(self.vm_handle,
                                                        timeout,
                                                        None,
                                                        None)
        err = self.vix.VixJob_Wait(job_handle,None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'waiting tools in the guest error {0}'.format(err)
            raise VMError(message)                 
        
class Guest(object):
    """
    class realizes guest machine interface
    """
    def __init__(self, vix, vm_handle):
        self.vix = vix
        self.vm_handle = vm_handle
        self.VIX_OK = 0        

    def run_cscript_in_guest(self, script_text):
        """
        login to the guest OS and executes cscript script in the guest
        """   
        job_handle = self.vix.VixVM_RunScriptInGuest(self.vm_handle,
                                                     Constants.CSCRIPT_INTERPRETER,
                                                     script_text,
                                                     0,
                                                     0,
                                                     None,
                                                     None)
        err = self.vix.VixJob_Wait(job_handle,None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'running script in the guest error {0}'.format(err)
            raise VMError(message) 
        
    def run_program_in_guest(self, program, keys):
        """
        runs program in guest
        """
        job_handle = self.vix.VixVM_RunProgramInGuest(self.vm_handle,
                                                      program.encode(),
                                                      keys.encode(),
                                                      0,
                                                      0,
                                                      None,
                                                      None)
        err = self.vix.VixJob_Wait(job_handle,None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'running program in the guest error {0}'.format(err)
            raise VMError(message)         

    def run_python_script_in_guest(self, script_text):
        """
        login to the guest OS and executes python script in the guest
        """   
        job_handle = self.vix.VixVM_RunScriptInGuest(self.vm_handle,
                                                     Constants.PYTHON_INTERPRETER,
                                                     script_text,
                                                     0,
                                                     0,
                                                     None,
                                                     None)
        err = self.vix.VixJob_Wait(job_handle,None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'running script in the guest error {0}'.format(err)
            raise VMError(message) 
        
    def copy_file_from_host_to_guest(self, dest_file_name, src_file_name):
        """
        copies file from host to guest
        """
        job_handle = self.vix.VixVM_CopyFileFromHostToGuest(self.vm_handle,
                                                            src_file_name.encode(),
                                                            dest_file_name.encode(),
                                                            0,
                                                            0,
                                                            None,
                                                            None)
        err = self.vix.VixJob_Wait(job_handle, None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'file copying error {0}'.format(err)
            raise VMError(message)
        
    def _logout_from_guest(self):
        """
        logs out from guest
        """
        job_handle = self.vix.VixVM_LogoutFromGuest(self.vm_handle,
                                                    None,
                                                    None)
        err = self.vix.VixJob_Wait(job_handle, None)
        self.vix.Vix_ReleaseHandle(job_handle)
        if err != self.VIX_OK:
            message = 'log out from guest error {0}'.format(err)
            raise VMError(message) 	                 
        
        
    def __del__(self):
        self._logout_from_guest()
                        
class VMError(Exception):                   
    pass
